/*
 ============================================================================
 Name        : Encoder.c
 Author      : Shah Hossain
 Version     :
 Copyright   : Your copyright notice
 Description : Variable size Hamming encoder
 ============================================================================
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <time.h>

#include "encoder_lib.h"
#include "decoder_lib.h"
#include "error_gen.h"
#include "packet_gen.h"
#include "Transmitter.h"

#define BYTE 8
int DEBUG=2;

char tx_buffer[250];
char rx_buffer[250];
int new_pkt=1; //control variable for new/existing packet


char *hamEncode(char*);
char *hamDecode(char*);
int addError(char*); //returns number of errors

double BER = 0; //must be non-zero
int seg_size = 2; //number of segments

int total_bytes_sent = 0;
int total_bytes_received =0;

int total_retransmission=0;

int test_error();
int test_hamming();
int test_packet();

void segment(int);
void aggregate(int);

enum reply {RETRANSMIT, SEGMENT, AGGREGATE, PKT_RECEIVED, SEG_RECEIVED}; //seg will not ask for retransmit && seg

enum reply receiver(char* packet, int errors)
{
	static int history[11] ={0,0,0,0,0,0,0,0,0,0,0};

	static int this_pkt=1;
	char *decode=NULL;
	int total_segments = pow(2,seg_size);
	int hist_sum=0;
	int i;


	for (i=0; i<10; i++)
		history[i]=history[i+1];

	history[10] = (errors>1);

	if (DEBUG==2) printf("History: ");
	for (i=0; i<11; i++)
	{
		hist_sum += history[i];
		if (DEBUG==2) printf("%d.",history[i]);
	}
	if (DEBUG==2)printf(" (Errors: %d)\n",errors);

	decode= hamDecode(packet);
	//printf("Decoded: \t\t");


	if (errors>1)
	{
		total_retransmission++;
		return RETRANSMIT;
	}
	else
	{
		printf("decode size: %d\n",getSize(decode));
		printStream(decode,1);
		printStream(packet,1);
		total_bytes_received += getSize(decode);

		int sz;

		if (this_pkt==1)
			sz=0;
		else
			sz=getSize(rx_buffer);

		strcpy(&rx_buffer[sz],decode);
		/*
		printf("Packet: ");
		printStream(packet,1);
		printf("Decoded: ");
		printStream(decode,1);
		*/
		if (DEBUG==2) printf("Iteration #%d/%d: ",this_pkt, total_segments);
		//printStream(rx_buffer,1);
		//printStream(decode,1);

		if (this_pkt==total_segments)
		{
			this_pkt=1;

			if (DEBUG==2) printf("Received Packet: ");
			if (DEBUG==2) printStream(rx_buffer,1);

			if(hist_sum>6)
				return SEGMENT;
			else if (hist_sum<1)
				return AGGREGATE;
			else
				return PKT_RECEIVED;

		}
	}


	this_pkt++;
	return SEG_RECEIVED;
}

char *transmitter(char *packet, int *errors)
{
	static int pkt_size;
	int total_segments = pow(2,seg_size);

	char *segment=NULL;
	static int this_pkt=1;
	static int seg_index=0;

	if(new_pkt)
	{
		//free(tx_buffer);
		this_pkt=1;
		new_pkt=0;
		seg_index=0;
		strcpy(tx_buffer,packet);
		pkt_size = getSize(tx_buffer)+1;
		if (DEBUG==2) puts("======================================================New PKT");

	}

	int div_size=((pkt_size+0.5)/total_segments);

	if(this_pkt==total_segments) //last packet
		new_pkt=1;
	//printf("pkt size: %d\tpkt num:%d\n",pkt_size,pkt_num);
	//printf("This PKT #: %d (%d)\tSeg index: %d (div %d)\n",this_pkt,pkt_num,seg_index,div_size);
	//printf("buffer: %s\n",tx_buffer);

	segment= (char *) malloc(div_size+1);
	strncpy(segment,&tx_buffer[seg_index],div_size);
	segment[div_size] = '\0';
	seg_index += div_size;
	if (DEBUG==2) printf("Segment %d: %s\n->",this_pkt,segment);
	//this_pkt++; //should not inc if retransmit


	//printf("%s\t",rand);
	if (DEBUG==2) printf("Segment: \t\t");
	if (DEBUG==2) printStream(segment,1);
	char *encode=hamEncode(segment);

	//printf("Encoded Segment: \t");
	//printStream(encode,1);

	*errors= addError(encode);
	//printf("Corrupted Segment: (%d) \t",*errors);
	//printStream(encode,1);

	if (*errors <2)
	{
		this_pkt++; //should not inc if retransmit
	}
	else
	{
		seg_index -= div_size;
		new_pkt=0;
	}

	printf("seg_size: %d\n",getSize(segment));
	total_bytes_sent += getSize(segment);
	return encode;
}


//bug sequences
//0111 0011 1001 0011 1001 0000
//1111 0010 0111 0011 1011 0000
//1110 0011 0101 1111 1001 0000

int main123()
{
	DEBUG=1;
	test_packet();
	DEBUG=0;

	return 1;
}


int main(void)
{
	char *rand = packet_gen2(50*BYTE,(char) 0x7E);
	char buffer[250]="Hello_worldHello_worldHello_worldHello_worldHello_worldHello_worldHello_worldHello_worldHello_world";
	//char *rand = packet_gen(10*BYTE);
	char *pkt=buffer;
	char *tx=NULL;
	enum reply rx_reply;

	sgenrand(time(NULL));
	//BER =  1*pow(10,2);
	BER =  700;
	int errors=-1;

	if (DEBUG==2) printf("BER: %1.2e\tSegment Size: %d\n",1/BER,(int) pow(2,seg_size));
	if (DEBUG==2) printf("Packet: %s\n",pkt);
	if (DEBUG==2) printStream(pkt,1);

	int i;
	for (i=0; i<10; i++)
	{
		do
		{
			do
			{
				tx=transmitter(pkt,&errors);
				rx_reply=receiver(tx,errors);
				printf("packet: %s\n",tx);
			}
			while (rx_reply==RETRANSMIT);
			printf("packet: %s\n",rx_buffer);
		}
		while (rx_reply==SEG_RECEIVED);

		if (rx_reply==SEGMENT)
		{
			if (seg_size < 5)
				seg_size++;
		}
		else if (rx_reply==AGGREGATE)
		{
			if (seg_size >0)
				seg_size--;
		}

		printf("\nPacket #%d\tReply: %d\t",i,(int)rx_reply);
		switch(rx_reply)
		{
			case RETRANSMIT: printf("RETRANSMIT"); break;
			case SEGMENT: printf("SEGMENT"); break;
			case AGGREGATE: printf("AGGREGATE"); break;
			case PKT_RECEIVED: printf("PKT_RECEIVED"); break;
			case SEG_RECEIVED: printf("SEG_RECEIVED"); break;
		}
		printf("\nNew Segment Size=%d\n",(int) pow(2,seg_size));


		if (strcmp(pkt,rx_buffer)==0)
			puts("Buffers Match :D");
		else
			puts("Buffers Don't Match :(");

		printf("Total bytes sent= %d\tTotal received= %d\tTotal retransmission=%d\n",total_bytes_sent,total_bytes_received,total_retransmission);

	}

	printf("Throughput= %f",(float) total_bytes_received/total_bytes_sent);

	return 1;

}


int main2(void)
{
	//char *rand = packet_gen(20*BYTE);  //making n+1 packets for n>2
	int i;
	char rand[]="ABD";
	char *encode=hamEncode(rand);
	char *encode2=hamEncode("B");

	enum reply rx_reply=receiver(encode,1);
	printf("REPLY: %d\n", (int) rx_reply);
	for (i=0;i<2; i++)
	{
		rx_reply=receiver(encode,5);
		printf("REPLY: %d\n", (int) rx_reply);
	}
	//printf("REPLY: %d\n", (int) rx_reply);
	rx_reply=receiver(encode2,1);
	printf("REPLY: %d\n", (int) rx_reply);

	puts(rand);
	puts(rx_buffer);

	return 1;
}

int main3(void)
{
	sgenrand(time(NULL));
	BER =  1*pow(10,2);
	int errors=-1;
	//char *rand = packet_gen(20*BYTE);  //making n+1 packets for n>2
	//changeBIT(rand,2,1);  //quick fix for trailing 0 bug
	//changeBIT(rand,0,1);  //quick fix for trailing 0 bug
	char rand[]="Hello_Packet";
	printf("Full Packet: %s",rand);
	printStream(rand,1);
	char *tx;

	do
	{
		tx=transmitter(rand,&errors);
		char *decode=hamDecode(tx);
		printf("Corrected Segment: \t");
		printStream(decode,1);
		puts("");
	}
	while (!new_pkt);


	char *decode = hamDecode(tx);



	printf("Errors: %d\t",errors);

	//if (errors< 2)
		//printf("STRINGS EQUAL= %d\n",!strcmp(rand,decode));

	return 1;
}


int test_packet (void)
{

	srand(time(NULL));
	char *encode, *decode;

	//31 limit on 7E
	char *rand = packet_gen2(50*BYTE,(char) 0x7E);

	//30,32 bytes doesn't work
	//char *rand = packet_gen2(500*BYTE,0xF6);  //making n+1 packets for n>2
	//char *rand = packet_gen(40*BYTE);  //making n+1 packets for n>2
	changeBIT(rand,2,1);  //quick fix for trailing 0 bug
	printf("%s\t",rand);
	printStream(rand,1);
	encode=hamEncode(rand);
	decode=hamDecode(encode);
	printf("%s\t",rand);
	printStream(decode,1);

	printf("STRINGS EQUAL= %d\n",!strcmp(rand,decode));
	free(rand);

	return 1;
}



int test_error(void)
{
	sgenrand(time(NULL));
	int i=0;
	BER =  1*pow(10,3);
	char data[] ="dfasefdfasdfasdfasdfasdfsdfasdfasdfdsafdafsdfasdfasdfasd";
	printf("Size: %d\n",getSize(data)*8);

	for (i=0;i <100; i++)
	{
		int j=addError(data);
		printf("#%d Errors on run %d\n",j,i);
	}
	return 1;
}


int test_decoder(void)
{
	//char *rand = packet_gen(20*BYTE);  //making n+1 packets for n>2
	char *encode=hamEncode("AC");
	char *encode2=hamEncode("BD");

	printStream(encode2,1);
	char *decode = hamDecode(encode);
	char *decode2 = hamDecode(encode2);
	printStream(encode2,1);
	decode2 = hamDecode(encode2);
	printStream(encode2,1);
	puts(decode);
	puts(decode2);

	return 1;
}



int test_hamming(void) {

	//char data[]= {0x05,0x15,'\0'};
	//char data[]= {0x23,0x60,0x61,'\0'};
	char data[] ="HELLO";
	//unsigned char data[]= {0x08,0xE8,'\0'};
	char* ptr=data;
	char* decoded;

	ptr=hamEncode(ptr);
	printf("Encoded: \t"); printStream(ptr,1);
	//Add error here
	changeBIT(ptr,11,-1);
	printf("Corrupted:\t");printStream(ptr,1);

	printf("==================================================Decoder\n");
	decoded=hamDecode(ptr);
	printf("==================================================Output\n");
	printf("Input: \t%s\t",data);
	printStream(data,1);
	printf("Output:\t%s\t",decoded);
	printStream(decoded,1);
	printf("STRINGS EQUAL= %d\n",!strcmp(data,decoded));

	free(ptr);
	return 1;
}
